# coding: utf-8

from itertools import izip, groupby, chain
from itertools import permutations, product

def sort_cycle(l):
    minindex = l.index(min(l))
    return l[minindex:]+l[:minindex]

def uniq(iterable, key=None):
    for key, grp in groupby(iterable, key=key):
        yield grp.next()

def iter_base_period_seq(alphabet, period):
    perms = ((perm, sort_cycle(perm)) for perm in permutations(alphabet, period))
    for p, sp in uniq(sorted(perms, key=lambda i:i[1]), key=lambda i:i[1]):
        yield ''.join(p)
    
def iter_base_seqs(alphabet, period, win_length, parts_num=None):
    """
    Генерирует всевозможные варианты последовательностей длины win_length
    для создания базовых матриц с заданным уровнем точности:
      * iter_base_period_seq выдаёт все варианты последовательностей
        длины period из символов alphabet с учётом циклических перестановок
      * при parts_num = 1  - минимальная точность,
        при parts_num = len(alphabet) - средняя,
        при parts_num = win_length / period - максимальная (все варианты)
    """
    parts_num = parts_num or len(alphabet)
    assert win_length % period == 0    
    if parts_num > win_length / period:
        parts_num = win_length / period
    base_seq_parts = set( tuple(sorted(p))
        for p in product(iter_base_period_seq(alphabet, period), repeat=parts_num)
    )

    min_win_length_part = win_length / (period * parts_num) * period
    max_win_length_part = win_length - min_win_length_part * (parts_num - 1)
    win_length_parts = [min_win_length_part] * (parts_num - 1) + [max_win_length_part]

    for base_parts in base_seq_parts:
        seq = ""
        for base_seq, win_length_part in izip(base_parts, win_length_parts):
            seq += base_seq * (win_length_part/period)
        #print seq
        yield seq

